home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / texinfo / info / nodemenu.c < prev    next >
C/C++ Source or Header  |  1994-01-28  |  8KB  |  322 lines

  1. /* nodemenu.c -- Produce a menu of all visited nodes. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 Free Software Foundation, Inc.
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22.    Written by Brian Fox (bfox@ai.mit.edu). */
  23.  
  24. #include "info.h"
  25.  
  26. /* Return a line describing the format of a node information line. */
  27. static char *
  28. nodemenu_format_info ()
  29. {
  30.   return ("\n\
  31. * Menu:\n\
  32.   (File)Node                        Lines   Size   Containing File\n\
  33.   ----------                        -----   ----   ---------------");
  34. }
  35.  
  36. /* Produce a formatted line of information about NODE.  Here is what we want
  37.    the output listing to look like:
  38.  
  39. * Menu:
  40.   (File)Node                        Lines   Size   Containing File
  41.   ----------                        -----   ----   ---------------
  42. * (emacs)Buffers::                  48      2230   /usr/gnu/info/emacs/emacs-1
  43. * (autoconf)Writing configure.in::  123     58789  /usr/gnu/info/autoconf/autoconf-1
  44. * (dir)Top::                40      589    /usr/gnu/info/dir
  45. */
  46. static char *
  47. format_node_info (node)
  48.      NODE *node;
  49. {
  50.   register int i, len;
  51.   char *parent, *containing_file;
  52.   static char *line_buffer = (char *)NULL;
  53.  
  54.   if (!line_buffer)
  55.     line_buffer = (char *)xmalloc (1000);
  56.  
  57.   if (node->parent)
  58.     {
  59.       parent = filename_non_directory (node->parent);
  60.       if (!parent)
  61.     parent = node->parent;
  62.     }
  63.   else
  64.     parent = (char *)NULL;
  65.  
  66.   containing_file = node->filename;
  67.  
  68.   if (!parent && !*containing_file)
  69.     sprintf (line_buffer, "* %s::", node->nodename);
  70.   else
  71.     {
  72.       char *file = (char *)NULL;
  73.  
  74.       if (parent)
  75.     file = parent;
  76.       else
  77.     file = filename_non_directory (containing_file);
  78.  
  79.       if (!file)
  80.     file = containing_file;
  81.  
  82.       if (!*file)
  83.     file = "dir";
  84.  
  85.       sprintf (line_buffer, "* (%s)%s::", file, node->nodename);
  86.     }
  87.  
  88.   len = pad_to (36, line_buffer);
  89.  
  90.   {
  91.     int lines = 1;
  92.  
  93.     for (i = 0; i < node->nodelen; i++)
  94.       if (node->contents[i] == '\n')
  95.     lines++;
  96.  
  97.     sprintf (line_buffer + len, "%d", lines);
  98.   }
  99.  
  100.   len = pad_to (44, line_buffer);
  101.   sprintf (line_buffer + len, "%d", node->nodelen);
  102.  
  103.   if (node->filename && *(node->filename))
  104.     {
  105.       len = pad_to (51, line_buffer);
  106.       sprintf (line_buffer + len, node->filename);
  107.     }
  108.  
  109.   return (savestring (line_buffer));
  110. }
  111.  
  112. /* Little string comparison routine for qsort (). */
  113. static int
  114. compare_strings (string1, string2)
  115.      char **string1, **string2;
  116. {
  117.   return (stricmp (*string1, *string2));
  118. }
  119.  
  120. /* The name of the nodemenu node. */
  121. static char *nodemenu_nodename = "*Node Menu*";
  122.  
  123. /* Produce an informative listing of all the visited nodes, and return it
  124.    in a node.  If FILTER_FUNC is non-null, it is a function which filters
  125.    which nodes will appear in the listing.  FILTER_FUNC takes an argument
  126.    of NODE, and returns non-zero if the node should appear in the listing. */
  127. NODE *
  128. get_visited_nodes (filter_func)
  129.      Function *filter_func;
  130. {
  131.   register int i, iw_index;
  132.   INFO_WINDOW *info_win;
  133.   NODE *node;
  134.   char **lines = (char **)NULL;
  135.   int lines_index = 0, lines_slots = 0;
  136.  
  137.   if (!info_windows)
  138.     return ((NODE *)NULL);
  139.  
  140.   for (iw_index = 0; info_win = info_windows[iw_index]; iw_index++)
  141.     {
  142.       for (i = 0; i < info_win->nodes_index; i++)
  143.     {
  144.       node = info_win->nodes[i];
  145.  
  146.       /* We skip mentioning "*Node Menu*" nodes. */
  147.       if (internal_info_node_p (node) &&
  148.           (strcmp (node->nodename, nodemenu_nodename) == 0))
  149.         continue;
  150.  
  151.       if (node && (!filter_func || (*filter_func) (node)))
  152.         {
  153.           char *line;
  154.  
  155.           line = format_node_info (node);
  156.           add_pointer_to_array
  157.         (line, lines_index, lines, lines_slots, 20, char *);
  158.         }
  159.     }
  160.     }
  161.  
  162.   /* Sort the array of information lines. */
  163.   qsort (lines, lines_index, sizeof (char *), compare_strings);
  164.  
  165.   /* Delete duplicates. */
  166.   {
  167.     register int j, newlen;
  168.     char **temp;
  169.  
  170.     for (i = 0, newlen = 1; i < lines_index - 1; i++)
  171.       {
  172.     if (strcmp (lines[i], lines[i + 1]) == 0)
  173.       {
  174.         free (lines[i]);
  175.         lines[i] = (char *)NULL;
  176.       }
  177.     else
  178.       newlen++;
  179.       }
  180.  
  181.     /* We have free ()'d and marked all of the duplicate slots.  Copy the
  182.        live slots rather than pruning the dead slots. */
  183.     temp = (char **)xmalloc ((1 + newlen) * sizeof (char *));
  184.     for (i = 0, j = 0; i < lines_index; i++)
  185.       if (lines[i])
  186.     temp[j++] = lines[i];
  187.  
  188.     temp[j] = (char *)NULL;
  189.     free (lines);
  190.     lines = temp;
  191.     lines_index = newlen;
  192.   }
  193.  
  194.   initialize_message_buffer ();
  195.   printf_to_message_buffer
  196.     ("Here is a menu of nodes you could select with info-history-node:\n");
  197.   printf_to_message_buffer ("%s\n", nodemenu_format_info ());
  198.   for (i = 0; i < lines_index; i++)
  199.     {
  200.       printf_to_message_buffer ("%s\n", lines[i]);
  201.       free (lines[i]);
  202.     }
  203.   free (lines);
  204.  
  205.   node = message_buffer_to_node ();
  206.   add_gcable_pointer (node->contents);
  207.   return (node);
  208. }
  209.  
  210. DECLARE_INFO_COMMAND (list_visited_nodes,
  211.    "Make a window containing a menu of all of the currently visited nodes")
  212. {
  213.   WINDOW *new;
  214.   NODE *node;
  215.  
  216.   set_remembered_pagetop_and_point (window);
  217.  
  218.   /* If a window is visible and showing the buffer list already, re-use it. */
  219.   for (new = windows; new; new = new->next)
  220.     {
  221.       node = new->node;
  222.  
  223.       if (internal_info_node_p (node) &&
  224.       (strcmp (node->nodename, nodemenu_nodename) == 0))
  225.     break;
  226.     }
  227.  
  228.   /* If we couldn't find an existing window, try to use the next window
  229.      in the chain. */
  230.   if (!new && window->next)
  231.     new = window->next;
  232.  
  233.   /* If we still don't have a window, make a new one to contain the list. */
  234.   if (!new)
  235.     {
  236.       WINDOW *old_active;
  237.  
  238.       old_active = active_window;
  239.       active_window = window;
  240.       new = window_make_window ((NODE *)NULL);
  241.       active_window = old_active;
  242.     }
  243.  
  244.   /* If we couldn't make a new window, use this one. */
  245.   if (!new)
  246.     new = window;
  247.  
  248.   /* Lines do not wrap in this window. */
  249.   new->flags |= W_NoWrap;
  250.   node = get_visited_nodes ((Function *)NULL);
  251.   name_internal_node (node, nodemenu_nodename);
  252.  
  253.   /* Even if this is an internal node, we don't want the window
  254.      system to treat it specially.  So we turn off the internalness
  255.      of it here. */
  256.   node->flags &= ~N_IsInternal;
  257.  
  258.   /* If this window is already showing a node menu, reuse the existing node
  259.      slot. */
  260.   {
  261.     int remember_me = 1;
  262.  
  263. #if defined (NOTDEF)
  264.     if (internal_info_node_p (new->node) &&
  265.     (strcmp (new->node->nodename, nodemenu_nodename) == 0))
  266.       remember_me = 0;
  267. #endif /* NOTDEF */
  268.  
  269.     window_set_node_of_window (new, node);
  270.  
  271.     if (remember_me)
  272.       remember_window_and_node (new, node);
  273.   }
  274.  
  275.   active_window = new;
  276. }
  277.  
  278. DECLARE_INFO_COMMAND (select_visited_node,
  279.       "Select a node which has been previously visited in a visible window")
  280. {
  281.   char *line;
  282.   NODE *node;
  283.   REFERENCE **menu;
  284.  
  285.   node = get_visited_nodes ((Function *)NULL);
  286.  
  287.   menu = info_menu_of_node (node);
  288.   free (node);
  289.  
  290.   line =
  291.     info_read_completing_in_echo_area (window, "Select visited node: ", menu);
  292.  
  293.   window = active_window;
  294.  
  295.   /* User aborts, just quit. */
  296.   if (!line)
  297.     {
  298.       info_abort_key (window, 0, 0);
  299.       info_free_references (menu);
  300.       return;
  301.     }
  302.  
  303.   if (*line)
  304.     {
  305.       REFERENCE *entry;
  306.  
  307.       /* Find the selected label in the references. */
  308.       entry = info_get_labeled_reference (line, menu);
  309.  
  310.       if (!entry)
  311.     info_error ("The reference disappeared! (%s).", line);
  312.       else
  313.     info_select_reference (window, entry);
  314.     }
  315.  
  316.   free (line);
  317.   info_free_references (menu);
  318.  
  319.   if (!info_error_was_printed)
  320.     window_clear_echo_area ();
  321. }
  322.